package praktikumid.w11.p5;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

/**
 * Simple operations with objects.
 * A list of objects, where new objects can
 * be added, existing objects can be modified
 * and existing objects can be deleted.
 * <p>
 * We use this simple program to demonstrate
 * undo implementation possibilities.
 * <p>
 * This program is very simple and does not
 * handle any exceptions. This is so to 
 * keep the code simple. Of course, in real
 * world problems (and also in class exercises),
 * all exceptions should be handled properly!
 * <p>
 * The given class shows how undo functionality
 * can be added to this list of elements.
 * @author Ago
 *
 */
public class UndoHistory {
	/**
	 * Simple element, which technically only has
	 * one String - name. 
	 * @author Ago
	 *
	 */
	static class Element {
		/**
		 * Name of the element.
		 */
		String name;
		
		/**
		 * Constructor, where we set the name.
		 * @param name
		 */
		public Element(String name) {
			this.name = name;
		}
		
		/**
		 * Constructor, which uses another
		 * element object to copy all
		 * the information to a new element.
		 * In our case, we only have to fill
		 * in the value of name. Used to make
		 * a new copy of an existing element.
		 * @param copy
		 */
		public Element(Element copy) {
			name = copy.name;
		}
		
		@Override
		public String toString() {
			// let's have a "nice" printout
			return name;
		}
	}
	
	/**
	 * A list of elements.
	 */
	static List<Element> elements = new ArrayList<Element>();
	
	/**
	 * List of states. Every state is a list of elements.
	 */
	static List<List<Element>> history = new ArrayList<List<Element>>();
	/**
	 * Let's keep track of the index in history list.
	 * This is not needed if we only implement undo.
	 * If you want to add redo functionality, this
	 * index is used to move forward in the history list.
	 */
	static int historyIndex = -1;

	/**
	 * Main.
	 * @param args arguments.
	 * @throws IOException Dealing with input can throw exception.
	 */
	public static void main(String[] args) throws IOException {
		newState(); // empty state
		BufferedReader br = new BufferedReader(
				new InputStreamReader(System.in));
		while(true) {
			printHelp();
			String line = br.readLine();
			// we expect correct input
			// just split everything by space
			String[] tokens = line.split(" ");
			int op = Integer.valueOf(tokens[0]);
			int elid = -1;
			switch (op) {
			case 1: // add element
				elements.add(new Element(tokens[1]));
				break;
			case 2: // modify element, rename
				elid = Integer.valueOf(tokens[1]);
				Element e = elements.get(elid);
				e.name = tokens[2];
				break;
			case 3: // delete element
				elid = Integer.valueOf(tokens[1]);
				elements.remove(elid);
				break;
			case 4: // printout
				printElements();
				break;
			case 5:
				undo();
				printElements();
				break;
			case 0:
				System.exit(0);
			}
			
			if (op > 0 && op < 4) {
				// every time some action was taken,
				// we make a new state.
				// Notice, that we create a state
				// AFTER the operation (the same
				// should apply in your homework).
				newState();
			}

			
		}
		
	}
	
	/**
	 * Creates a new state. It copies all the elements
	 * into a new list and adds this new list to the 
	 * history list. If we have used undo (and
	 * historyIndex does not point to the last element
	 * in history), we have to remove all the states
	 * after the historyIndex. That is the case if we
	 * have undone 2 actions and make a new action. Those
	 * 2 undone actions we cannot access anymore 
	 * (that's how undo works). Therefore, we can
	 * remove those 2 obsolete states.
	 */
	public static void newState() {
		historyIndex++;
		while (historyIndex < history.size()) {
			// remove obsolete states
			history.remove(historyIndex);
		}
		List<Element> newElements = new ArrayList<Element>();
		for (Element e : elements) {
			// copies every element into a new list
			newElements.add(new Element(e));
		}
		history.add(newElements);
	}
	
	/**
	 * Undoes the last action. Technically
	 * it restores the state from history list.
	 * If we undo, the list from the history state
	 * is copied into our elements list. Copying 
	 * is important in order to leave the history
	 * states unchanged.
	 */
	public static void undo() {
		if (historyIndex > 0) {
			historyIndex--;
			elements.clear();
			for (Element e : history.get(historyIndex)) {
				elements.add(new Element(e));
			}
		}
	}
	
	/**
	 * Prints out the elements in the list.
	 */
	public static void printElements() {
		System.out.println("Elements:");
		for (int i = 0; i < elements.size(); i++) {
			System.out.println(i + ". " + elements.get(i));
		}
	}
	
	/**
	 * Prints out help - what can be done with the program.
	 */
	public static void printHelp() {
		System.out.println("  1 add element [1 name]");
		System.out.println("  2 change element [2 elid name]");
		System.out.println("  3 delete element [3 elid]");
		System.out.println("  4 list elements [4]");
		System.out.println("  5 undo [5]");
		System.out.println("  0 exit [0]");
	}
}

